package org.pc2r.network;

import java.io.Closeable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.pc2r.exception.PC2RException;
import org.pc2r.game.GameManager;
import org.pc2r.game.GameStore;
import org.pc2r.logging.FrontLogger;
import org.pc2r.protocole.EnumRequest;
import org.pc2r.protocole.RequestBuilder;
import org.pc2r.protocole.message.WelcomeConnectMessage;
import org.pc2r.util.SystemUtil;

/**
 * Le serveur principal de l'application qui reçoit toutes les requêtes.
 * 
 */
public class FrontServer implements Runnable, Closeable {

  static final private int PORT = 2012;

  private final Logger logger;
  private final GameStore store;
  private ServerSocket server;
  private int port;

  public FrontServer(GameStore store) {
    this(FrontLogger.get(), store);
  }

  public FrontServer(Logger logger, GameStore store) {
    this(logger, PORT, store);
  }

  public FrontServer(Logger logger, int port, GameStore store) {
    this.port = port;
    assert logger != null && store != null;
    this.logger = logger;
    this.store = store;
  }

  @Override
  public void run() {
    // On crée le serveur
    try {
      server = new ServerSocket(port);
    } catch (IOException e1) {
      logger.log(Level.SEVERE, "Une réseau est survenue", e1);
      System.exit(-1);// On kill si le serveur est down
      return;
    }
    logger.info("Démarrage du serveur sur le port " + port);
    // Tant que le serveur est bindé, on itère. Cela permet de le couper
    // proprement
    while (server.isBound()) {
      Socket socket = null;
      try {
        // On récupère la socket
        socket = server.accept();
        // On parse ce qu'à envoyer le client        
        SocketReadWriter readWriter = new SocketReadWriter(socket);
        String request = readWriter.read(true);
        if (request == null || request.isEmpty())
          continue; // Erreur gérée dans getRequest

        WelcomeConnectMessage parseConnect = RequestBuilder.parseConnect(request);
        // On enregistre l'utilisateur dans la partie
        GameManager orCreate = store.getOrCreate();
        String name = parseConnect.getName();
        name = orCreate.addPlayer(name, readWriter);
        // On renvoie un welcome au client que l'on a connecté
        WelcomeConnectMessage welcomeConnectMessage = new WelcomeConnectMessage(EnumRequest.WELCOME, name);
        readWriter.write(welcomeConnectMessage);
      } catch (IOException e) {
        if (!server.isClosed()) {
          String msg = String.format("Une réseau est survenue pour le client d'adresse %s.", socket.getInetAddress().getHostName());
          logger.log(Level.SEVERE, msg, e);
          SystemUtil.close(socket);
        }
      } catch (PC2RException ex) {
        logger.log(Level.SEVERE, ex.getMessage(), ex);
        // Si la requête CONNECT est fausse, on envoie un accessdenied
        String accessDenied = EnumRequest.ACCESSDENIED.toString() + RequestBuilder.SEPARATOR + RequestBuilder.END;
        try {
          socket.getOutputStream().write(accessDenied.getBytes());
        } catch (IOException e) {
        } // On catch l'erreur silencieusement
        // Si on ne peut pas lui envoyer de réponse, on arrête les frais
        // et on kill la connexion tout de suite
        SystemUtil.close(socket);
      }
    }
  }

  @Override
  public void close() {
    logger.info("Le serveur est en cours de fermeture...");
    SystemUtil.close(server);
    logger.info("Le serveur est éteint.");
  }

}
